{-# LANGUAGE OverloadedStrings #-}

import Control.Exception (bracket)
import Control.Monad
import Data.Time.Clock (diffUTCTime, getCurrentTime)
import Data.Time.Format (defaultTimeLocale, formatTime)
import Options.Applicative
import System.Directory (doesFileExist, getHomeDirectory, removeFile)
import System.Environment (getExecutablePath)
import System.FilePath (replaceExtension, takeDirectory, (</>))
import System.Process (createProcess, proc, waitForProcess)

data AppArgs = AppArgs
  { folders :: [FilePath],
    output :: FilePath,
    exclude :: Bool,
    excludePatterns :: [String]
  }
  deriving (Show)

main :: IO ()
main = do
  args <- execParser opts
  _currentDir <- takeDirectory <$> getExecutablePath
  home <- getHomeDirectory
  currentTime <- getCurrentTime -- 获取当前时间
  let timeStamp = formatTime defaultTimeLocale "%y%m%d_%H%M%S" currentTime -- 格式化时间
  let desktop = home </> "Desktop"
  let outputFile =
        if null (output args)
          then desktop </> ("casesZip_" ++ timeStamp ++ ".7z")
          else desktop </> replaceExtension (output args) "7z"
  putStrLn $ "<<<[ymy]: 打包到压缩文件: " ++ outputFile

  -- 检查并删除旧压缩文件
  fileExists <- doesFileExist outputFile
  when fileExists $ do
    putStrLn "<<<<< remove old archive"
    removeFile outputFile
    putStrLn "<<<<< remove done"

  -- 构建排除参数
  let baseExcludes =
        if exclude args
          then
            [ "-xr!cache",
              "-xr!__pycache__",
              "-xr!result_backup",
              "-xr!result - 副本"
            ]
          else []
      extraExcludes = map ("-xr!" ++) (excludePatterns args)
      allExcludes = baseExcludes ++ extraExcludes

  -- 执行7z压缩
  putStrLn "<<<<< start process 7z a:"
  let sevenZipExe = "C:/Program Files/7-Zip/7z.exe"
      argsList = ["a", outputFile] ++ allExcludes ++ folders args

  bracket
    ( do
        start <- getCurrentTime
        (_, _, _, ph) <- createProcess (proc sevenZipExe argsList)
        return (ph, start)
    )
    (\(ph, _) -> waitForProcess ph)
    ( \(ph, start) -> do
        _ret <- waitForProcess ph
        end <- getCurrentTime
        let duration = realToFrac (diffUTCTime end start) :: Double
        putStrLn $ "zip time cost: " ++ show duration ++ " seconds"
    )
  where
    opts =
      info
        (argsParser <**> helper)
        ( fullDesc
            <> progDesc "将给定的 folders 添加到 xxx.7z压缩文档, 并排除 result 等结果目录"
            <> header "my_archive_7z - 7z压缩工具"
        )

    argsParser :: Parser AppArgs
    argsParser =
      AppArgs
        <$> some (argument str (metavar "FOLDER..." <> help "要加入压缩包的目录, 可以有多个"))
        <*> strOption
          ( long "output"
              <> short 'o'
              <> metavar "NAME"
              <> value ""
              <> help "指定输出的 .7z 文档的名称, 不带.7z后缀; 默认自动创建名称"
          )
        <*> switch
          ( long "exclude"
              <> short 'x'
              <> help "开关, 是否排除 result 等结果目录"
          )
        <*> many
          ( strOption
              ( long "exclude-pattern"
                  <> short 'p'
                  <> metavar "NAME"
                  <> help "模式列表, 要排除的其他目录的模式"
              )
          )
